#include "tabview.h"
#include <allegro5/allegro_primitives.h>
#include <iostream>
#include <cassert>
#include "font.h"
using namespace std;

TabView::TabView(Rect rect, Font *pFont) 
: View(rect, pFont), m_paintTabs(true) {
     setRect(rect);
}

void TabView::createTab(const std::string &name, View *pView) {
     m_tabs[name] = pView;
     // make hidden to begin with
     m_tabs[name]->hide();
     // set view rect to viewport size
     m_tabs[name]->setRect(m_viewRect);
     m_tabOrder.push_back(name);
}

void TabView::setActiveTab(const std::string &name) {
     if (name == m_activeTab)
	  return;
     if (!m_tabs[name])
	  return;
     // hide old view and show new one
     if (m_tabs[m_activeTab])
	  m_tabs[m_activeTab]->hide();
     m_activeTab = name;
     m_tabs[m_activeTab]->show();
     m_paintTabs = true;
//     m_tabs[m_activeTab]->refresh();
     m_tabs[m_activeTab]->paint(true);
//     refresh();
}

void TabView::setRect(Rect r) {
     m_rect = r;
     m_viewRect = r;
     Rect tr = tabRect();
     m_viewRect.setY(tr.y() + tr.height() + 1);
     m_viewRect.setHeight(m_rect.height() - tr.height() - 1);
     map<string,View*>::iterator iter;
     for (iter=m_tabs.begin(); iter!=m_tabs.end(); iter++) {
	  if (iter->second)
	       iter->second->setRect(m_viewRect);
     }
}

void TabView::paint(bool all) {
     if (m_paintTabs || all)
	  paintTabs();
     if (m_tabs[m_activeTab] && (m_tabs[m_activeTab]->needPaint() || all)) {
	  m_tabs[m_activeTab]->paint(all);
     }
}

// return rect of whole tab control area
Rect TabView::tabRect() {
     return Rect(m_rect.x(), m_rect.y(), m_rect.width(),
		 m_pFont->height() + TabView::Padding * 2);
}

// get a single tab's rect by name
Rect TabView::tabRect(const string &name) {
     Rect r = tabRect();
     Rect tabRect(r.x(), r.y(), 0, r.height());
     for (unsigned int i=0; i<m_tabOrder.size(); i++) {
	  tabRect.setWidth(m_tabOrder[i].length() * m_pFont->width() + TabView::Padding * 2);
	  if (m_tabOrder[i] == name)
	       return tabRect;
	  tabRect.setX(tabRect.x() + tabRect.width());
     }
     return Rect();
}

void TabView::paintTabs() {
     // save the old clipping rectangle
     int oldx=0, oldy=0, oldw=0, oldh=0;
     al_get_clipping_rectangle(&oldx, &oldy, &oldw, &oldh);
     Rect r = tabRect();
     // clear
     al_draw_filled_rectangle(r.x(), r.y(), 
			      r.x() + r.width(), 
			      r.y() + r.height(),
			      al_map_rgb(0,0,0));
     // draw bottom line
     al_draw_line(r.x(), r.y() + r.height(), r.x() + r.width(), r.y() + r.height(), 
		  al_map_rgb(255,255,255), TabView::LineThickness);
     // draw tabs
     Rect tr, fr(0,0,0,m_pFont->height());
     for (map<string,View*>::iterator iter=m_tabs.begin(); iter!=m_tabs.end(); iter++) {
	  // get rects
	  tr = tabRect(iter->first);
	  fr.setX(tr.x() + TabView::Padding);
	  fr.setY(tr.y() + TabView::Padding);
	  fr.setWidth(iter->first.length() * m_pFont->width());
          // draw border
	  al_set_clipping_rectangle(tr.x() - 1, tr.y() - 1, 
				    tr.width() + 2, tr.height());
	  al_draw_rounded_rectangle(tr.x(), tr.y() + 1, 
				    tr.x() + tr.width(),
				    tr.y() + tr.height() + TabView::Radius,
				    TabView::Radius, TabView::Radius, al_map_rgb(255,255,255),
				    TabView::LineThickness);
	  al_set_clipping_rectangle(oldx, oldy, oldw, oldh);
	  // print tab name
	  m_pFont->print(iter->first, fr);
	  // erase bottom line if painting the active tab
	  if (iter->first == m_activeTab) {
	       al_draw_line(tr.x() + TabView::LineThickness, 
			    r.y() + r.height(), 
			    tr.x() + tr.width(),
			    r.y() + r.height(), al_map_rgb(0,0,0),
			    TabView::LineThickness);
	  }
     }
     m_paintTabs = false;
}

void TabView::keyDown(int keycode, int modifiers, int unichar) {
     if (m_tabs[m_activeTab])
	  m_tabs[m_activeTab]->keyDown(keycode, modifiers, unichar);
}

void TabView::mouseDown(Point p, int button) {
     map<string,View*>::iterator iter;
     bool inTab = false;
     for (iter=m_tabs.begin(); iter!=m_tabs.end(); iter++) {
	  if (tabRect(iter->first).inside(p)) {
	       setActiveTab(iter->first);
	       inTab = true;
	       break;
	  }
     }
     if (!inTab && m_tabs[m_activeTab] && m_tabs[m_activeTab]->rect().inside(p))
	  m_tabs[m_activeTab]->mouseDown(p, button);
}

void TabView::mouseUp(Point p, int button) {
     if (m_tabs[m_activeTab] && m_tabs[m_activeTab]->rect().inside(p))
	  m_tabs[m_activeTab]->mouseUp(p, button);
}

void TabView::mouseMove(Point p, Point dp) {
     if (!m_tabs[m_activeTab])
	  return;
     if (m_tabs[m_activeTab]->rect().inside(p) || m_tabs[m_activeTab]->grabMouse())
	  m_tabs[m_activeTab]->mouseMove(p, dp);
}

void TabView::mouseEnter(Point p) {
     if (m_tabs[m_activeTab] && m_tabs[m_activeTab]->rect().inside(p))
	  m_tabs[m_activeTab]->mouseEnter(p);
}

void TabView::mouseExit(Point p) {
     if (m_tabs[m_activeTab] && m_tabs[m_activeTab]->rect().inside(p))
	  m_tabs[m_activeTab]->mouseExit(p);
}

bool TabView::grabMouse() {
     if (m_tabs[m_activeTab])
	  return m_tabs[m_activeTab]->grabMouse();
     return false;
}

bool TabView::needPaint() {
     if (m_needPaint || (m_tabs[m_activeTab] && m_tabs[m_activeTab]->needPaint()))
	  return true;
     return false;
}

void TabView::update() {
     if (!m_tabs[m_activeTab])
	  return;
     m_tabs[m_activeTab]->update();
}
 
bool TabView::grabKeys() {
     assert(m_tabs[m_activeTab]);
     return m_tabs[m_activeTab]->grabKeys();
}

bool TabView::hasFocus() {
     return (m_hasFocus || (m_tabs[m_activeTab] && m_tabs[m_activeTab]->hasFocus()));
}

void TabView::focus() {
     m_hasFocus = true;
     if (m_tabs[m_activeTab])
	  m_tabs[m_activeTab]->focus();
}

void TabView::blur() {
     m_hasFocus = false;
     if (m_tabs[m_activeTab])
	  m_tabs[m_activeTab]->blur();
}
